package com.dlmap.readbook.views;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabFolder2Adapter;
import org.eclipse.swt.custom.CTabFolder2Listener;
import org.eclipse.swt.custom.CTabFolderEvent;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.themes.ITheme;

import com.dlmap.readbook.ReadbookPlugin;

/**
 * This sample class demonstrates how to plug-in a new workbench view. The view
 * shows data obtained from the model. The sample creates a dummy model on the
 * fly, but a real implementation would connect to the model available either in
 * this or another plug-in (e.g. the workspace). The view is connected to the
 * model using a content provider.
 * <p>
 * The view uses a label provider to define how model objects should be
 * presented in the view. Each view can present the same model objects using
 * different labels and icons, if needed. Alternatively, a single label provider
 * can be shared between views in order to ensure that objects of the same type
 * are presented in the same way everywhere.
 * <p>
 */

public class ReadbookView extends ViewPart implements IPropertyChangeListener,
		DisposeListener {
	//
	private static class Note {

		private CTabItem tab;

		private Text text;

		private boolean wrap;

		public Note(CTabFolder parent, boolean wrap, boolean showClose) {
			this(parent, parent.getItemCount(), wrap, showClose);
		}

		public Note(CTabFolder parent, int index, boolean wrap,
				boolean showClose) {
			this.tab = new CTabItem(parent, showClose ? SWT.CLOSE : SWT.NONE,
					index);
			this.wrap = wrap;
			this.text = createText(parent);
			tab.setControl(text);

			realoadApearence();
		}

		private Text createText(Composite parent) {
			Text ret = new Text(parent, SWT.MULTI
					| (wrap ? SWT.WRAP : SWT.H_SCROLL) | SWT.V_SCROLL);
			configuteText(ret);
			return ret;
		}

		public void setFocus() {
			text.setFocus();
		}

		public String getContents() {
			return text.getText();
		}

		public void setContents(String content) {
			text.setText(content);
		}

		public void dispose() {
			text.dispose();
			// o tab � liberado pelo tabFolder
		}

		public void addFocusListener(FocusListener listener) {
			text.addFocusListener(listener);
		}

		public void addModifyListener(ModifyListener listener) {
			text.addModifyListener(listener);
		}

		public boolean getWrap() {
			return wrap;
		}

		public void setName(String name) {
			tab.setText(name);
		}

		public String getName() {
			return tab.getText();
		}

		public void setWrap(boolean wrap) {
			this.wrap = wrap;

			// tem que recriar o text para poder trocar o wrap
			Text newText = createText(tab.getParent());
			newText.setText(text.getText());

			tab.setControl(newText);

			// atualiza a sele��o, tem que ser depois de setar o controle do
			// item
			newText.setSelection(text.getSelection());

			// libera o text antigo
			text.dispose();
			text = newText;
		}

		public void setShowClose(boolean show) {
			// tab.setShowClose(show); // only in eclipse >= 3.4

			String label = tab.getText();
			Control control = tab.getControl();
			int index = tab.getParent().indexOf(tab);
			CTabFolder parent = tab.getParent();
			tab.dispose();

			CTabItem newTab = new CTabItem(parent, show ? SWT.CLOSE : SWT.NONE,
					index);
			newTab.setText(label);
			newTab.setControl(control);
			tab = newTab;

			parent.redraw();
		}

		private void configuteText(Text txt) {
			ITheme theme = ReadbookPlugin.getDefault().getWorkbench()
					.getThemeManager().getCurrentTheme();
			// configura a fonte
			txt.setFont(theme.getFontRegistry().get(PREF_TEXT_FONT));
			// configura a cor da fonte
			txt.setForeground(theme.getColorRegistry().get(PREF_TEXT_COLOR));
			// configura a cor do fundo
			txt.setBackground(theme.getColorRegistry()
					.get(PREF_TEXT_BACKGROUND));
		}

		public void realoadApearence() {
			configuteText(text);
		}

	}

	private static final String KEY_TEXT_NAME = "text.name.note#";

	private static final String KEY_TEXT_CONTENTS = "text.contents.note#";

	private static final String KEY_TEXT_WRAP = "text.wrap.note#";

	private static final String KEY_TAB_COUNT = "tab.count";

	private static final String KEY_TAB_SELECTED = "tab.selected";

	private static final String PREF_TEXT_FONT = ReadbookPlugin.PLUGIN_ID
			+ ".text.font";

	private static final String PREF_TEXT_COLOR = ReadbookPlugin.PLUGIN_ID
			+ ".text.color";

	private static final String PREF_TEXT_BACKGROUND = ReadbookPlugin.PLUGIN_ID
			+ ".text.background";

	private static final String NOTE_NAME = "Note #";

	private CTabFolder tabFolder;

	private final List<Note> notes = new ArrayList<Note>();

	private Action wrapAction;

	private Action clearAction;

	private Action readAction;

	public ReadbookView() {
		// nop
	}

	@Override
	public void createPartControl(Composite parent) {
		// carrega configura��o
		IDialogSettings settings = ReadbookPlugin.getDefault()
				.getDialogSettings();

		int tabCount;
		try {
			tabCount = settings.getInt(KEY_TAB_COUNT);
		} catch (NumberFormatException e) {
			tabCount = 1;
		}
		int selectedTab;
		try {
			selectedTab = settings.getInt(KEY_TAB_SELECTED);
		} catch (NumberFormatException e) {
			selectedTab = 0;
		}

		// cria tab folder
		tabFolder = new CTabFolder(parent, SWT.BORDER | SWT.BOTTOM);
		// tabFolder.setMRUVisible(true);

		// cria as tabs
		for (int i = 0; i < tabCount; i++) {
			addTab(i, true, tabCount > 1);
		}
		// cria bot�o de criar nova tab
		addNewTabItem();

		// seta a tab selecionada
		tabFolder.setSelection(selectedTab);

		tabFolder.addSelectionListener(getSelectionListener());
		tabFolder.addCTabFolder2Listener(getTabFolder2Listener());
		tabFolder.addMouseListener(getMouseListener());

		makeActions();
		contributeToActionBars();

		ReadbookPlugin.getDefault().getWorkbench().getThemeManager()
				.addPropertyChangeListener(this);
		tabFolder.addDisposeListener(this);
	}

	private MouseListener getMouseListener() {
		return new MouseAdapter() {

			@Override
			public void mouseDoubleClick(MouseEvent e) {
				if (e.button != 1) {
					return;
				}
				CTabItem item = tabFolder.getItem(new Point(e.x, e.y));
				if (item == null) {
					return;
				}
				final Note note = notes.get(tabFolder.indexOf(item));
				final String originalName = note.getName();
				final Text text = new Text(tabFolder, SWT.NONE);
				text.setText(originalName);
				text.selectAll();

				Rectangle bounds = item.getBounds();
				adjustBounds(bounds);
				text.setBounds(bounds);

				text.setFocus();

				text.addFocusListener(new FocusAdapter() {

					@Override
					public void focusLost(FocusEvent e1) {
						if (text.getText().length() == 0) {
							note.setName(originalName);
						}
						text.dispose();
						note.setFocus();
					}

				});

				text.addModifyListener(new ModifyListener() {

					public void modifyText(ModifyEvent e1) {
						note.setName(text.getText());

						// reajusta o tamanho do text
						Rectangle b = tabFolder.getSelection().getBounds();
						adjustBounds(b);
						text.setBounds(b);
					}

				});

				text.addKeyListener(new KeyAdapter() {

					@Override
					public void keyReleased(KeyEvent e1) {
						switch (e1.keyCode) {
						case SWT.ESC:
							// restaura o nome original
							note.setName(originalName);
							text.dispose();
							note.setFocus();
							break;
						case SWT.CR:
							// se o text estiver vazio, n�o deixa sair
							if (text.getText().length() > 0) {
								text.dispose();
								note.setFocus();
								break;
							}
						}
					}

				});
			}

			private void adjustBounds(Rectangle bounds) {
				bounds.x += 1;
				bounds.width -= 2;
				bounds.y += 1;
				bounds.height -= 2;
			}
		};
	}

	private CTabFolder2Listener getTabFolder2Listener() {
		return new CTabFolder2Adapter() {

			@Override
			public void close(CTabFolderEvent event) {
				int index = tabFolder.indexOf((CTabItem) event.item);
				Note note = notes.get(index);
				boolean empty = note.getContents().length() == 0;
				event.doit = empty
						|| MessageDialog.openQuestion(tabFolder.getShell(),
								"Notepad", //
								"Do you really want to close \""
										+ note.getName() + "\"?");
				if (event.doit) {
					// remove o text da tab fechada e atualiza os nomes
					Note n = notes.remove(index);
					n.dispose();
					if (notes.size() == 1) {
						notes.get(0).setShowClose(false);
					}
				}
			}

		};
	}

	private SelectionListener getSelectionListener() {
		return new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				CTabItem item = (CTabItem) e.item;
				int selIndex = tabFolder.indexOf(item);

				// se for o bot�o para nova tab
				if (item.getControl() == null) {
					// adiciona uma tab com edit
					addTab(notes.size(), false, true);

					// seleciona a tab criada
					tabFolder.setSelection(selIndex);

					if (notes.size() == 2) {
						notes.get(0).setShowClose(true);
					}
				}
				Note note = notes.get(selIndex);

				// joga o foco no text
				note.setFocus();

				// atualiza o wordwrap
				wrapAction.setChecked(note.getWrap());
				clearAction.setEnabled(note.getContents().length() > 0);
			}
		};
	}

	private String nextTabName() {
		int i = 0;
		for (Note note : notes) {
			String name = note.getName();
			if (name.startsWith(NOTE_NAME)) {
				name = name.substring(NOTE_NAME.length());
				try {
					int idx = Integer.parseInt(name);
					if (idx > i) {
						i = idx;
					}
				} catch (NumberFormatException e) {
					// ignore
				}
			}
		}

		return NOTE_NAME + (i + 1);
	}

	private void addNewTabItem() {
		CTabItem newTabItem = new CTabItem(tabFolder, SWT.NONE);
		newTabItem.setImage(ReadbookPlugin.getImageDescriptor(
				ReadbookPlugin.IMG_ETOOL_ADD_TAB).createImage());
		newTabItem.setToolTipText("New Note");
	}

	private void addTab(int index, boolean load, boolean showClose) {
		IDialogSettings settings = ReadbookPlugin.getDefault()
				.getDialogSettings();

		boolean wrap = true;
		if (load && settings.get(KEY_TEXT_WRAP + index) != null) {
			wrap = settings.getBoolean(KEY_TEXT_WRAP + index);
		}

		// cria a tab
		Note note = new Note(tabFolder, index, wrap, showClose);

		String name = null;
		// carrega os texto salvo nas configura��es
		if (load) {
			String contents = settings.get(KEY_TEXT_CONTENTS + index);
			if (contents != null) {
				note.setContents(contents);
			}
			name = settings.get(KEY_TEXT_NAME + index);
		}
		if (name == null) {
			name = nextTabName();
		}
		note.setName(name);

		// salva o texto sempre que perder o foco,
		// a chance de ter salvo o conte�do se o eclipse travar � muito maior
		// assim
		note.addFocusListener(new FocusAdapter() {

			@Override
			public void focusLost(FocusEvent e) {
				saveTexts();
			}

		});
		note.addModifyListener(new ModifyListener() {

			public void modifyText(ModifyEvent e) {
				clearAction
						.setEnabled(((Text) e.widget).getText().length() > 0);
			}
		});

		// adiciona o note na lista
		notes.add(note);
	}

	private void saveTexts() {
		IDialogSettings settings = ReadbookPlugin.getDefault()
				.getDialogSettings();
		for (int i = 0; i < notes.size(); i++) {
			Note note = notes.get(i);
			settings.put(KEY_TEXT_NAME + i, note.getName());
			settings.put(KEY_TEXT_CONTENTS + i, note.getContents());
			settings.put(KEY_TEXT_WRAP + i, note.getWrap());
		}
		settings.put(KEY_TAB_COUNT, notes.size());
		settings.put(KEY_TAB_SELECTED, tabFolder.getSelectionIndex());
		// TODO: armazenar a sele��o dos texts (inicio e fim)
		ReadbookPlugin.getDefault().saveDialogSettings();
	}

	public void propertyChange(PropertyChangeEvent event) {
		if (PREF_TEXT_FONT.equals(event.getProperty()) || //
				PREF_TEXT_COLOR.equals(event.getProperty()) || //
				PREF_TEXT_BACKGROUND.equals(event.getProperty())) {
			for (Note note : notes) {
				note.realoadApearence();
			}
		}
	}

	public void widgetDisposed(final DisposeEvent e) {
		if (tabFolder == e.widget) {
			ReadbookPlugin.getDefault().getWorkbench().getThemeManager()
					.removePropertyChangeListener(this);
		}
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		manager.add(wrapAction);
		manager.add(new Separator());
		manager.add(clearAction);
		manager.add(readAction);
	}

	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(wrapAction);
		manager.add(clearAction);
	}

	private void makeActions() {
		wrapAction = new Action("", Action.AS_CHECK_BOX) {

			@Override
			public void run() {
				Note activeNote = getActiveNote();
				activeNote.setWrap(!activeNote.getWrap());

				wrapAction.setChecked(activeNote.getWrap());

				getActiveNote().setFocus();
			}
		};
		wrapAction.setText("Wrap Text");
		wrapAction.setToolTipText("Wrap Text");
		wrapAction.setImageDescriptor(ReadbookPlugin
				.getImageDescriptor(ReadbookPlugin.IMG_ETOOL_WRAP));
		wrapAction.setChecked(getActiveNote().getWrap());

		clearAction = new Action() {

			@Override
			public void run() {
				boolean clear = MessageDialog.openQuestion(
						tabFolder.getShell(), "Notepad", //
						"Do you really want to clear the text from \""
								+ getActiveNote().getName() + "\"?");
				if (clear) {
					getActiveNote().setContents("");
				}

				getActiveNote().setFocus();
			}
		};
		clearAction.setText("Clear Note");
		clearAction.setToolTipText("Clear Note");
		clearAction.setImageDescriptor(ReadbookPlugin
				.getImageDescriptor(ReadbookPlugin.IMG_ETOOL_CLEAR));
		clearAction.setDisabledImageDescriptor(ReadbookPlugin
				.getImageDescriptor(ReadbookPlugin.IMG_DTOOL_CLEAR));
		clearAction.setEnabled(getActiveNote().getContents().length() > 0);

		readAction = new Action() {

			@Override
			public void run() {
				WebSiteDialog dlg = new WebSiteDialog(tabFolder.getShell());
				// dlg.open();
				if (dlg.open() == Window.OK) {
					StringBuffer sb = new StringBuffer("");
					try {
						WebContent web = new WebContent();
						String tmp = web
								.getOneHtml(dlg.getInputText());
						List<String> li = web.getCC(tmp);
						for (int i = 0; i < li.size(); i++) {
							sb.append(web.outTag(web.br2nl(li.get(i))));
						}
					} catch (Exception ex) {

					}
					getActiveNote().setContents(sb.toString());
					getActiveNote().setFocus();
				}
			}
		};

		readAction.setText("Read Note");
		readAction.setToolTipText("Read Note");
		readAction.setImageDescriptor(ReadbookPlugin
				.getImageDescriptor(ReadbookPlugin.IMG_ETOOL_CLEAR));
		readAction.setDisabledImageDescriptor(ReadbookPlugin
				.getImageDescriptor(ReadbookPlugin.IMG_DTOOL_CLEAR));
		
	}

	@Override
	public void setFocus() {
		getActiveNote().setFocus();
	}

	private Note getActiveNote() {
		return notes.get(tabFolder.getSelectionIndex());
	}

}